<!DOCTYPE html>
<html lang="en-US">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>揭秘软件开发的达摩克利斯之剑 | 鱼皮的编程宝典</title>
    <meta name="generator" content="VuePress 1.9.10">
    <link rel="icon" href="/favicon.ico">
    <script>
        var _hmt = _hmt || [];
        (function() {
          var hm = document.createElement("script");
          hm.src = "https://hm.baidu.com/hm.js?2675818a983a3131404cee835018f016";
          var s = document.getElementsByTagName("script")[0]; 
          s.parentNode.insertBefore(hm, s);
        })();
      </script>
    <meta name="description" content="贴心的编程学习路线，全面的编程知识百科">
    <meta property="article:modified_time" content="2023-11-11T12:17:47.000Z">
    <meta property="og:site_name" content="鱼皮的编程宝典">
    <meta property="og:title" content="揭秘软件开发的达摩克利斯之剑">
    <meta property="og:type" content="article">
    <meta property="og:url" content="https://codefather.cn/%E6%8F%AD%E7%A7%98%E8%BD%AF%E4%BB%B6%E5%BC%80%E5%8F%91%E7%9A%84%E8%BE%BE%E6%91%A9%E5%85%8B%E5%88%A9%E6%96%AF%E4%B9%8B%E5%89%91/">
    <meta name="twitter:title" content="揭秘软件开发的达摩克利斯之剑">
    <meta name="twitter:url" content="https://codefather.cn/%E6%8F%AD%E7%A7%98%E8%BD%AF%E4%BB%B6%E5%BC%80%E5%8F%91%E7%9A%84%E8%BE%BE%E6%91%A9%E5%85%8B%E5%88%A9%E6%96%AF%E4%B9%8B%E5%89%91/">
    <meta name="twitter:card" content="summary_large_image">
    <meta name="twitter:label1" content="Written by">
    <meta name="twitter:label2" content="Filed under">
    <meta name="twitter:data2" content="程序员, 编程, 计算机">
    <meta property="article:tag" content="程序员">
    <meta name="keywords" content="程序员鱼皮, 编程学习路线, 编程知识百科, Java, 编程导航, 前端, 开发, 编程分享, 项目, IT, 求职, 面经">
    
    <link rel="preload" href="/assets/css/0.styles.40c15e22.css" as="style"><link rel="preload" href="/assets/js/app.12ab4756.js" as="script"><link rel="preload" href="/assets/js/2.fd06b53e.js" as="script"><link rel="preload" href="/assets/js/268.e18ed861.js" as="script"><link rel="prefetch" href="/assets/js/1.17ea1209.js"><link rel="prefetch" href="/assets/js/10.d9532d8f.js"><link rel="prefetch" href="/assets/js/100.064ea126.js"><link rel="prefetch" href="/assets/js/101.d64c5b33.js"><link rel="prefetch" href="/assets/js/102.5e29a62c.js"><link rel="prefetch" href="/assets/js/103.59ad6cd7.js"><link rel="prefetch" href="/assets/js/104.ae4763e8.js"><link rel="prefetch" href="/assets/js/105.2a9124be.js"><link rel="prefetch" href="/assets/js/106.529bca07.js"><link rel="prefetch" href="/assets/js/107.c8671a0d.js"><link rel="prefetch" href="/assets/js/108.55c93c64.js"><link rel="prefetch" href="/assets/js/109.7d1efa54.js"><link rel="prefetch" href="/assets/js/11.2f89b5eb.js"><link rel="prefetch" href="/assets/js/110.0caecbf5.js"><link rel="prefetch" href="/assets/js/111.a2aeae49.js"><link rel="prefetch" href="/assets/js/112.eb7ceda5.js"><link rel="prefetch" href="/assets/js/113.f304408d.js"><link rel="prefetch" href="/assets/js/114.988c637f.js"><link rel="prefetch" href="/assets/js/115.bfdeed31.js"><link rel="prefetch" href="/assets/js/116.a47b8c53.js"><link rel="prefetch" href="/assets/js/117.2e1e8250.js"><link rel="prefetch" href="/assets/js/118.758ce408.js"><link rel="prefetch" href="/assets/js/119.73d9e688.js"><link rel="prefetch" href="/assets/js/12.53aaa509.js"><link rel="prefetch" href="/assets/js/120.202a8b2e.js"><link rel="prefetch" href="/assets/js/121.ca91d59a.js"><link rel="prefetch" href="/assets/js/122.8804c0dd.js"><link rel="prefetch" href="/assets/js/123.ceada225.js"><link rel="prefetch" href="/assets/js/124.4b2718b7.js"><link rel="prefetch" href="/assets/js/125.72134235.js"><link rel="prefetch" href="/assets/js/126.167b162c.js"><link rel="prefetch" href="/assets/js/127.f8139c17.js"><link rel="prefetch" href="/assets/js/128.da64a377.js"><link rel="prefetch" href="/assets/js/129.c07963b1.js"><link rel="prefetch" href="/assets/js/13.52940164.js"><link rel="prefetch" href="/assets/js/130.f66fc8bb.js"><link rel="prefetch" href="/assets/js/131.a913c7dd.js"><link rel="prefetch" href="/assets/js/132.ea648654.js"><link rel="prefetch" href="/assets/js/133.074ca70e.js"><link rel="prefetch" href="/assets/js/134.dc79ddd8.js"><link rel="prefetch" href="/assets/js/135.c4516208.js"><link rel="prefetch" href="/assets/js/136.2546baf7.js"><link rel="prefetch" href="/assets/js/137.56454fc6.js"><link rel="prefetch" href="/assets/js/138.bdb002bf.js"><link rel="prefetch" href="/assets/js/139.40a2f9b5.js"><link rel="prefetch" href="/assets/js/14.ade1e74d.js"><link rel="prefetch" href="/assets/js/140.06b20427.js"><link rel="prefetch" href="/assets/js/141.8dd992e3.js"><link rel="prefetch" href="/assets/js/142.d9305485.js"><link rel="prefetch" href="/assets/js/143.41bf907e.js"><link rel="prefetch" href="/assets/js/144.c138960c.js"><link rel="prefetch" href="/assets/js/145.fc5b38c8.js"><link rel="prefetch" href="/assets/js/146.c9166b70.js"><link rel="prefetch" href="/assets/js/147.c31d8a01.js"><link rel="prefetch" href="/assets/js/148.5c0534ca.js"><link rel="prefetch" href="/assets/js/149.d2355ea2.js"><link rel="prefetch" href="/assets/js/15.970a8ca5.js"><link rel="prefetch" href="/assets/js/150.c1644898.js"><link rel="prefetch" href="/assets/js/151.b6e4adf0.js"><link rel="prefetch" href="/assets/js/152.7a5071aa.js"><link rel="prefetch" href="/assets/js/153.31abbc68.js"><link rel="prefetch" href="/assets/js/154.9c6bb079.js"><link rel="prefetch" href="/assets/js/155.e5506a40.js"><link rel="prefetch" href="/assets/js/156.992dab50.js"><link rel="prefetch" href="/assets/js/157.12a6ee84.js"><link rel="prefetch" href="/assets/js/158.84dc1045.js"><link rel="prefetch" href="/assets/js/159.fd2022b1.js"><link rel="prefetch" href="/assets/js/16.ff971500.js"><link rel="prefetch" href="/assets/js/160.8d8889cd.js"><link rel="prefetch" href="/assets/js/161.31797def.js"><link rel="prefetch" href="/assets/js/162.bfae7a75.js"><link rel="prefetch" href="/assets/js/163.e5e8094d.js"><link rel="prefetch" href="/assets/js/164.870f212c.js"><link rel="prefetch" href="/assets/js/165.8da20a31.js"><link rel="prefetch" href="/assets/js/166.950a187d.js"><link rel="prefetch" href="/assets/js/167.fd042c52.js"><link rel="prefetch" href="/assets/js/168.7d9f058d.js"><link rel="prefetch" href="/assets/js/169.205df80e.js"><link rel="prefetch" href="/assets/js/17.4b045b26.js"><link rel="prefetch" href="/assets/js/170.270108a3.js"><link rel="prefetch" href="/assets/js/171.9f03d27d.js"><link rel="prefetch" href="/assets/js/172.1882bac7.js"><link rel="prefetch" href="/assets/js/173.fc1fe7d4.js"><link rel="prefetch" href="/assets/js/174.d77c927b.js"><link rel="prefetch" href="/assets/js/175.019b9e83.js"><link rel="prefetch" href="/assets/js/176.5ce31bd8.js"><link rel="prefetch" href="/assets/js/177.9a2006f2.js"><link rel="prefetch" href="/assets/js/178.88265ac1.js"><link rel="prefetch" href="/assets/js/179.3205ce07.js"><link rel="prefetch" href="/assets/js/18.1cdd0514.js"><link rel="prefetch" href="/assets/js/180.2ff51f44.js"><link rel="prefetch" href="/assets/js/181.f792ce97.js"><link rel="prefetch" href="/assets/js/182.103328e1.js"><link rel="prefetch" href="/assets/js/183.40fde303.js"><link rel="prefetch" href="/assets/js/184.1f36ac78.js"><link rel="prefetch" href="/assets/js/185.cf3b1c00.js"><link rel="prefetch" href="/assets/js/186.f7981399.js"><link rel="prefetch" href="/assets/js/187.20fe296d.js"><link rel="prefetch" href="/assets/js/188.7b004fea.js"><link rel="prefetch" href="/assets/js/189.599ca2d9.js"><link rel="prefetch" href="/assets/js/19.5740e4d6.js"><link rel="prefetch" href="/assets/js/190.14087c07.js"><link rel="prefetch" href="/assets/js/191.b0f73f6d.js"><link rel="prefetch" href="/assets/js/192.9d171669.js"><link rel="prefetch" href="/assets/js/193.00975585.js"><link rel="prefetch" href="/assets/js/194.2c7c43b4.js"><link rel="prefetch" href="/assets/js/195.ff126254.js"><link rel="prefetch" href="/assets/js/196.6546ae1c.js"><link rel="prefetch" href="/assets/js/197.eedb67bb.js"><link rel="prefetch" href="/assets/js/198.acdcc22c.js"><link rel="prefetch" href="/assets/js/199.7993a6b2.js"><link rel="prefetch" href="/assets/js/20.8885a4fa.js"><link rel="prefetch" href="/assets/js/200.52f08765.js"><link rel="prefetch" href="/assets/js/201.b85ab10b.js"><link rel="prefetch" href="/assets/js/202.ccec1c85.js"><link rel="prefetch" href="/assets/js/203.3f50a6ed.js"><link rel="prefetch" href="/assets/js/204.f28b803b.js"><link rel="prefetch" href="/assets/js/205.c51c53fd.js"><link rel="prefetch" href="/assets/js/206.ba0aa8f6.js"><link rel="prefetch" href="/assets/js/207.0dce311a.js"><link rel="prefetch" href="/assets/js/208.0ea629a6.js"><link rel="prefetch" href="/assets/js/209.f48c3e71.js"><link rel="prefetch" href="/assets/js/21.0bb62db4.js"><link rel="prefetch" href="/assets/js/210.689db8f2.js"><link rel="prefetch" href="/assets/js/211.72bc0f1f.js"><link rel="prefetch" href="/assets/js/212.456135bd.js"><link rel="prefetch" href="/assets/js/213.d0ea50fc.js"><link rel="prefetch" href="/assets/js/214.1400da91.js"><link rel="prefetch" href="/assets/js/215.aeef3e5d.js"><link rel="prefetch" href="/assets/js/216.a60215af.js"><link rel="prefetch" href="/assets/js/217.d6cd8b69.js"><link rel="prefetch" href="/assets/js/218.1bd500fc.js"><link rel="prefetch" href="/assets/js/219.20faf611.js"><link rel="prefetch" href="/assets/js/22.3e8347a6.js"><link rel="prefetch" href="/assets/js/220.6714179b.js"><link rel="prefetch" href="/assets/js/221.fb5b643d.js"><link rel="prefetch" href="/assets/js/222.d96272e5.js"><link rel="prefetch" href="/assets/js/223.4cb7dc3d.js"><link rel="prefetch" href="/assets/js/224.500d94cc.js"><link rel="prefetch" href="/assets/js/225.d006c34e.js"><link rel="prefetch" href="/assets/js/226.73612150.js"><link rel="prefetch" href="/assets/js/227.b02db9d1.js"><link rel="prefetch" href="/assets/js/228.aad43ff2.js"><link rel="prefetch" href="/assets/js/229.65340802.js"><link rel="prefetch" href="/assets/js/23.ceb5a5ff.js"><link rel="prefetch" href="/assets/js/230.97cab104.js"><link rel="prefetch" href="/assets/js/231.8415349a.js"><link rel="prefetch" href="/assets/js/232.db6d2697.js"><link rel="prefetch" href="/assets/js/233.723d05b1.js"><link rel="prefetch" href="/assets/js/234.26ed5e94.js"><link rel="prefetch" href="/assets/js/235.a586122b.js"><link rel="prefetch" href="/assets/js/236.f802bda8.js"><link rel="prefetch" href="/assets/js/237.cc8767ad.js"><link rel="prefetch" href="/assets/js/238.6485459e.js"><link rel="prefetch" href="/assets/js/239.9fbf3a55.js"><link rel="prefetch" href="/assets/js/24.07267ac6.js"><link rel="prefetch" href="/assets/js/240.1188f244.js"><link rel="prefetch" href="/assets/js/241.cbfb154d.js"><link rel="prefetch" href="/assets/js/242.352cea5a.js"><link rel="prefetch" href="/assets/js/243.07947e1c.js"><link rel="prefetch" href="/assets/js/244.f5b26fa9.js"><link rel="prefetch" href="/assets/js/245.c6030e32.js"><link rel="prefetch" href="/assets/js/246.d3fe99f7.js"><link rel="prefetch" href="/assets/js/247.185ae95d.js"><link rel="prefetch" href="/assets/js/248.a5f1548c.js"><link rel="prefetch" href="/assets/js/249.19691113.js"><link rel="prefetch" href="/assets/js/25.15e1f641.js"><link rel="prefetch" href="/assets/js/250.72fa1d33.js"><link rel="prefetch" href="/assets/js/251.bedd56f7.js"><link rel="prefetch" href="/assets/js/252.32bbcc8b.js"><link rel="prefetch" href="/assets/js/253.b59e5846.js"><link rel="prefetch" href="/assets/js/254.c0e37e1a.js"><link rel="prefetch" href="/assets/js/255.850da3cc.js"><link rel="prefetch" href="/assets/js/256.952d9817.js"><link rel="prefetch" href="/assets/js/257.88667bb8.js"><link rel="prefetch" href="/assets/js/258.c5873c28.js"><link rel="prefetch" href="/assets/js/259.6ae1dcbc.js"><link rel="prefetch" href="/assets/js/26.35b2bbdc.js"><link rel="prefetch" href="/assets/js/260.cb940cc3.js"><link rel="prefetch" href="/assets/js/261.0686c3e9.js"><link rel="prefetch" href="/assets/js/262.d0719839.js"><link rel="prefetch" href="/assets/js/263.ae83ebc1.js"><link rel="prefetch" href="/assets/js/264.209ad8a3.js"><link rel="prefetch" href="/assets/js/265.be8d1ee8.js"><link rel="prefetch" href="/assets/js/266.c51bceaa.js"><link rel="prefetch" href="/assets/js/267.fe8af48a.js"><link rel="prefetch" href="/assets/js/269.7126031e.js"><link rel="prefetch" href="/assets/js/27.270fcb06.js"><link rel="prefetch" href="/assets/js/270.89ead106.js"><link rel="prefetch" href="/assets/js/271.0643d07a.js"><link rel="prefetch" href="/assets/js/272.32a8e4b0.js"><link rel="prefetch" href="/assets/js/273.9881da47.js"><link rel="prefetch" href="/assets/js/274.2de022ea.js"><link rel="prefetch" href="/assets/js/275.d9a4fe99.js"><link rel="prefetch" href="/assets/js/276.d33de2e9.js"><link rel="prefetch" href="/assets/js/277.1ec367ab.js"><link rel="prefetch" href="/assets/js/278.66ab7c11.js"><link rel="prefetch" href="/assets/js/279.ff51cd15.js"><link rel="prefetch" href="/assets/js/28.ef3f6db2.js"><link rel="prefetch" href="/assets/js/280.ef45ed5f.js"><link rel="prefetch" href="/assets/js/281.87f26b81.js"><link rel="prefetch" href="/assets/js/282.c8ef594b.js"><link rel="prefetch" href="/assets/js/283.15643091.js"><link rel="prefetch" href="/assets/js/284.c4f25b2e.js"><link rel="prefetch" href="/assets/js/285.2d16e4b6.js"><link rel="prefetch" href="/assets/js/286.44eba266.js"><link rel="prefetch" href="/assets/js/287.7613e62f.js"><link rel="prefetch" href="/assets/js/288.b9546d55.js"><link rel="prefetch" href="/assets/js/289.125f85d8.js"><link rel="prefetch" href="/assets/js/29.177558be.js"><link rel="prefetch" href="/assets/js/290.db375797.js"><link rel="prefetch" href="/assets/js/291.90946626.js"><link rel="prefetch" href="/assets/js/292.a4524678.js"><link rel="prefetch" href="/assets/js/293.3ed9b083.js"><link rel="prefetch" href="/assets/js/294.d6c14116.js"><link rel="prefetch" href="/assets/js/295.2b485032.js"><link rel="prefetch" href="/assets/js/296.29d9aa0f.js"><link rel="prefetch" href="/assets/js/297.bfd72435.js"><link rel="prefetch" href="/assets/js/298.fc056989.js"><link rel="prefetch" href="/assets/js/299.0a3f258e.js"><link rel="prefetch" href="/assets/js/3.4dc033c7.js"><link rel="prefetch" href="/assets/js/30.248a229d.js"><link rel="prefetch" href="/assets/js/300.d064d776.js"><link rel="prefetch" href="/assets/js/301.d95b9fc0.js"><link rel="prefetch" href="/assets/js/302.1afa637a.js"><link rel="prefetch" href="/assets/js/303.231beaa8.js"><link rel="prefetch" href="/assets/js/304.05df35d9.js"><link rel="prefetch" href="/assets/js/305.f2d83cf9.js"><link rel="prefetch" href="/assets/js/306.d54f5e5e.js"><link rel="prefetch" href="/assets/js/307.d5df9000.js"><link rel="prefetch" href="/assets/js/308.ef2f52b3.js"><link rel="prefetch" href="/assets/js/309.d1f13319.js"><link rel="prefetch" href="/assets/js/31.32a62b91.js"><link rel="prefetch" href="/assets/js/310.30b84be3.js"><link rel="prefetch" href="/assets/js/311.d79e3145.js"><link rel="prefetch" href="/assets/js/312.58e24f19.js"><link rel="prefetch" href="/assets/js/313.9809a122.js"><link rel="prefetch" href="/assets/js/314.e92e41b1.js"><link rel="prefetch" href="/assets/js/315.8c2fc1d4.js"><link rel="prefetch" href="/assets/js/316.a0904343.js"><link rel="prefetch" href="/assets/js/317.18e42b54.js"><link rel="prefetch" href="/assets/js/318.6b051d27.js"><link rel="prefetch" href="/assets/js/319.513b0197.js"><link rel="prefetch" href="/assets/js/32.23b75afc.js"><link rel="prefetch" href="/assets/js/320.ae91bc36.js"><link rel="prefetch" href="/assets/js/321.a194efea.js"><link rel="prefetch" href="/assets/js/322.8273020e.js"><link rel="prefetch" href="/assets/js/323.d332e08f.js"><link rel="prefetch" href="/assets/js/324.ed867b64.js"><link rel="prefetch" href="/assets/js/325.c43ccf9a.js"><link rel="prefetch" href="/assets/js/326.9497d879.js"><link rel="prefetch" href="/assets/js/327.7f095e40.js"><link rel="prefetch" href="/assets/js/328.cb5c5847.js"><link rel="prefetch" href="/assets/js/329.d36b59f3.js"><link rel="prefetch" href="/assets/js/33.b258b779.js"><link rel="prefetch" href="/assets/js/330.b1a092b9.js"><link rel="prefetch" href="/assets/js/331.ff86566f.js"><link rel="prefetch" href="/assets/js/332.0daafa9f.js"><link rel="prefetch" href="/assets/js/333.b7e50524.js"><link rel="prefetch" href="/assets/js/334.7dcf9f0b.js"><link rel="prefetch" href="/assets/js/335.862c410a.js"><link rel="prefetch" href="/assets/js/336.c6384990.js"><link rel="prefetch" href="/assets/js/337.ab9d5e52.js"><link rel="prefetch" href="/assets/js/338.598ae59e.js"><link rel="prefetch" href="/assets/js/339.9b810ff8.js"><link rel="prefetch" href="/assets/js/34.3bd60f1f.js"><link rel="prefetch" href="/assets/js/340.5609a53c.js"><link rel="prefetch" href="/assets/js/341.969d335a.js"><link rel="prefetch" href="/assets/js/342.43f85dba.js"><link rel="prefetch" href="/assets/js/343.47280ef9.js"><link rel="prefetch" href="/assets/js/344.346ae5fc.js"><link rel="prefetch" href="/assets/js/345.32166361.js"><link rel="prefetch" href="/assets/js/346.86ff128b.js"><link rel="prefetch" href="/assets/js/347.3c5421fe.js"><link rel="prefetch" href="/assets/js/348.95910300.js"><link rel="prefetch" href="/assets/js/349.004158d8.js"><link rel="prefetch" href="/assets/js/35.18e8f66a.js"><link rel="prefetch" href="/assets/js/350.e10b195b.js"><link rel="prefetch" href="/assets/js/351.12f89875.js"><link rel="prefetch" href="/assets/js/352.83957394.js"><link rel="prefetch" href="/assets/js/353.475971b9.js"><link rel="prefetch" href="/assets/js/354.8af7b26b.js"><link rel="prefetch" href="/assets/js/355.85925e24.js"><link rel="prefetch" href="/assets/js/356.1d77cf9c.js"><link rel="prefetch" href="/assets/js/357.b467d481.js"><link rel="prefetch" href="/assets/js/358.ac96f32d.js"><link rel="prefetch" href="/assets/js/359.e048bd10.js"><link rel="prefetch" href="/assets/js/36.a7ae257c.js"><link rel="prefetch" href="/assets/js/360.e9b45545.js"><link rel="prefetch" href="/assets/js/361.ad1bb45b.js"><link rel="prefetch" href="/assets/js/362.4e24a30b.js"><link rel="prefetch" href="/assets/js/363.64f7dad5.js"><link rel="prefetch" href="/assets/js/364.d9e3ebc2.js"><link rel="prefetch" href="/assets/js/365.330e3086.js"><link rel="prefetch" href="/assets/js/366.a6c0afb3.js"><link rel="prefetch" href="/assets/js/367.ffcefa40.js"><link rel="prefetch" href="/assets/js/368.664c8e4d.js"><link rel="prefetch" href="/assets/js/369.67da2dd2.js"><link rel="prefetch" href="/assets/js/37.d709f9a9.js"><link rel="prefetch" href="/assets/js/370.b1791970.js"><link rel="prefetch" href="/assets/js/371.743a461a.js"><link rel="prefetch" href="/assets/js/372.7703ef1b.js"><link rel="prefetch" href="/assets/js/373.17de31f6.js"><link rel="prefetch" href="/assets/js/374.e508be9e.js"><link rel="prefetch" href="/assets/js/375.6e2e9fe3.js"><link rel="prefetch" href="/assets/js/376.8ca3511a.js"><link rel="prefetch" href="/assets/js/377.bfcee39c.js"><link rel="prefetch" href="/assets/js/378.21852b78.js"><link rel="prefetch" href="/assets/js/379.9649c307.js"><link rel="prefetch" href="/assets/js/38.68ca920e.js"><link rel="prefetch" href="/assets/js/380.dadb4418.js"><link rel="prefetch" href="/assets/js/381.a03c993d.js"><link rel="prefetch" href="/assets/js/382.bb7c22c1.js"><link rel="prefetch" href="/assets/js/383.bd68b2e5.js"><link rel="prefetch" href="/assets/js/384.f6dc7457.js"><link rel="prefetch" href="/assets/js/385.14287a91.js"><link rel="prefetch" href="/assets/js/386.a6284ac2.js"><link rel="prefetch" href="/assets/js/387.c51f147a.js"><link rel="prefetch" href="/assets/js/388.06d9651e.js"><link rel="prefetch" href="/assets/js/389.2d85d927.js"><link rel="prefetch" href="/assets/js/39.de850db9.js"><link rel="prefetch" href="/assets/js/390.2397b3e0.js"><link rel="prefetch" href="/assets/js/391.8413aaaf.js"><link rel="prefetch" href="/assets/js/392.00eecaa4.js"><link rel="prefetch" href="/assets/js/393.351dd3fd.js"><link rel="prefetch" href="/assets/js/394.d2cc4a70.js"><link rel="prefetch" href="/assets/js/395.abee64bf.js"><link rel="prefetch" href="/assets/js/396.bc265e6a.js"><link rel="prefetch" href="/assets/js/397.e4a96944.js"><link rel="prefetch" href="/assets/js/398.f0d26b29.js"><link rel="prefetch" href="/assets/js/399.e77c3ddc.js"><link rel="prefetch" href="/assets/js/4.2f502b73.js"><link rel="prefetch" href="/assets/js/40.91635261.js"><link rel="prefetch" href="/assets/js/400.c7eac401.js"><link rel="prefetch" href="/assets/js/401.06092d16.js"><link rel="prefetch" href="/assets/js/402.4e08e496.js"><link rel="prefetch" href="/assets/js/403.deba4c77.js"><link rel="prefetch" href="/assets/js/404.9100e4df.js"><link rel="prefetch" href="/assets/js/405.e9f451e2.js"><link rel="prefetch" href="/assets/js/406.a0da4aa4.js"><link rel="prefetch" href="/assets/js/407.58b2b123.js"><link rel="prefetch" href="/assets/js/408.eb2cde2d.js"><link rel="prefetch" href="/assets/js/409.9041f749.js"><link rel="prefetch" href="/assets/js/41.169a3cfc.js"><link rel="prefetch" href="/assets/js/410.c12f3710.js"><link rel="prefetch" href="/assets/js/411.452cda45.js"><link rel="prefetch" href="/assets/js/412.8691317a.js"><link rel="prefetch" href="/assets/js/413.25a68f21.js"><link rel="prefetch" href="/assets/js/414.7a9699d8.js"><link rel="prefetch" href="/assets/js/415.6e8cdaff.js"><link rel="prefetch" href="/assets/js/416.e8a86529.js"><link rel="prefetch" href="/assets/js/417.28330bde.js"><link rel="prefetch" href="/assets/js/418.2fed8e17.js"><link rel="prefetch" href="/assets/js/419.773032f4.js"><link rel="prefetch" href="/assets/js/42.e17cd061.js"><link rel="prefetch" href="/assets/js/420.56681228.js"><link rel="prefetch" href="/assets/js/421.0d170e13.js"><link rel="prefetch" href="/assets/js/422.fc8c2a11.js"><link rel="prefetch" href="/assets/js/423.e34ea6a6.js"><link rel="prefetch" href="/assets/js/424.20fe8748.js"><link rel="prefetch" href="/assets/js/425.f001b7cd.js"><link rel="prefetch" href="/assets/js/426.a91230ef.js"><link rel="prefetch" href="/assets/js/427.c6c9fe8d.js"><link rel="prefetch" href="/assets/js/428.9d4f5e41.js"><link rel="prefetch" href="/assets/js/429.c81cfd3f.js"><link rel="prefetch" href="/assets/js/43.f88aa667.js"><link rel="prefetch" href="/assets/js/430.eb61372f.js"><link rel="prefetch" href="/assets/js/431.4a25365a.js"><link rel="prefetch" href="/assets/js/432.42fc1bbe.js"><link rel="prefetch" href="/assets/js/433.8e9ac9f0.js"><link rel="prefetch" href="/assets/js/434.fdd0b160.js"><link rel="prefetch" href="/assets/js/435.d6cbac31.js"><link rel="prefetch" href="/assets/js/436.17750ba2.js"><link rel="prefetch" href="/assets/js/437.a1468099.js"><link rel="prefetch" href="/assets/js/438.ac6a45c2.js"><link rel="prefetch" href="/assets/js/439.242ae27a.js"><link rel="prefetch" href="/assets/js/44.3f36e228.js"><link rel="prefetch" href="/assets/js/440.b80b520c.js"><link rel="prefetch" href="/assets/js/441.e51a4bbb.js"><link rel="prefetch" href="/assets/js/442.ab0b3eda.js"><link rel="prefetch" href="/assets/js/443.47e44250.js"><link rel="prefetch" href="/assets/js/444.54c3b425.js"><link rel="prefetch" href="/assets/js/445.e006c12e.js"><link rel="prefetch" href="/assets/js/446.f2b79730.js"><link rel="prefetch" href="/assets/js/447.2509a397.js"><link rel="prefetch" href="/assets/js/448.b7d69dfc.js"><link rel="prefetch" href="/assets/js/449.8a575d9b.js"><link rel="prefetch" href="/assets/js/45.be1455b1.js"><link rel="prefetch" href="/assets/js/450.ad421aeb.js"><link rel="prefetch" href="/assets/js/451.f09912ae.js"><link rel="prefetch" href="/assets/js/452.f528c4ce.js"><link rel="prefetch" href="/assets/js/453.fc4dc0f9.js"><link rel="prefetch" href="/assets/js/454.733c315c.js"><link rel="prefetch" href="/assets/js/455.44fe35b1.js"><link rel="prefetch" href="/assets/js/456.7f650035.js"><link rel="prefetch" href="/assets/js/457.77ebc183.js"><link rel="prefetch" href="/assets/js/458.133078f4.js"><link rel="prefetch" href="/assets/js/459.33728b5e.js"><link rel="prefetch" href="/assets/js/46.5ad74cb5.js"><link rel="prefetch" href="/assets/js/460.6946f564.js"><link rel="prefetch" href="/assets/js/461.6f906c5c.js"><link rel="prefetch" href="/assets/js/462.26eca27e.js"><link rel="prefetch" href="/assets/js/463.e02e5f65.js"><link rel="prefetch" href="/assets/js/464.0df25c63.js"><link rel="prefetch" href="/assets/js/465.80cd249e.js"><link rel="prefetch" href="/assets/js/466.a0921e86.js"><link rel="prefetch" href="/assets/js/467.1bec2b47.js"><link rel="prefetch" href="/assets/js/468.b6fa37c1.js"><link rel="prefetch" href="/assets/js/469.a1d2487e.js"><link rel="prefetch" href="/assets/js/47.03df5d0a.js"><link rel="prefetch" href="/assets/js/470.36a4b9c8.js"><link rel="prefetch" href="/assets/js/471.28895273.js"><link rel="prefetch" href="/assets/js/472.b9fe392f.js"><link rel="prefetch" href="/assets/js/473.a2835d9c.js"><link rel="prefetch" href="/assets/js/474.cd57b37d.js"><link rel="prefetch" href="/assets/js/475.8d5a9792.js"><link rel="prefetch" href="/assets/js/476.f7f3781c.js"><link rel="prefetch" href="/assets/js/477.9b710b04.js"><link rel="prefetch" href="/assets/js/478.46d66687.js"><link rel="prefetch" href="/assets/js/479.e7444705.js"><link rel="prefetch" href="/assets/js/48.48f286ba.js"><link rel="prefetch" href="/assets/js/480.da2b750a.js"><link rel="prefetch" href="/assets/js/481.838a8e04.js"><link rel="prefetch" href="/assets/js/482.02bf8041.js"><link rel="prefetch" href="/assets/js/483.bb29ef2f.js"><link rel="prefetch" href="/assets/js/484.aebb68bd.js"><link rel="prefetch" href="/assets/js/485.a66ab41b.js"><link rel="prefetch" href="/assets/js/486.8df516fb.js"><link rel="prefetch" href="/assets/js/487.86d28130.js"><link rel="prefetch" href="/assets/js/488.0863da70.js"><link rel="prefetch" href="/assets/js/489.5d4d9e88.js"><link rel="prefetch" href="/assets/js/49.1ca52d11.js"><link rel="prefetch" href="/assets/js/490.3fd65d85.js"><link rel="prefetch" href="/assets/js/491.ae3a2f87.js"><link rel="prefetch" href="/assets/js/492.6f54679b.js"><link rel="prefetch" href="/assets/js/493.a594b1f4.js"><link rel="prefetch" href="/assets/js/494.4f03fae1.js"><link rel="prefetch" href="/assets/js/495.ae50b66b.js"><link rel="prefetch" href="/assets/js/496.bc92d835.js"><link rel="prefetch" href="/assets/js/497.eac65251.js"><link rel="prefetch" href="/assets/js/498.1d726726.js"><link rel="prefetch" href="/assets/js/499.01e4f7d1.js"><link rel="prefetch" href="/assets/js/5.7b1f056c.js"><link rel="prefetch" href="/assets/js/50.73732fe2.js"><link rel="prefetch" href="/assets/js/500.062ade4e.js"><link rel="prefetch" href="/assets/js/501.e6a711dc.js"><link rel="prefetch" href="/assets/js/502.c79e28e2.js"><link rel="prefetch" href="/assets/js/503.bf97bce9.js"><link rel="prefetch" href="/assets/js/504.b2784ef0.js"><link rel="prefetch" href="/assets/js/505.0767ba54.js"><link rel="prefetch" href="/assets/js/506.8ae14637.js"><link rel="prefetch" href="/assets/js/507.003e2349.js"><link rel="prefetch" href="/assets/js/508.ecb5e6bd.js"><link rel="prefetch" href="/assets/js/509.b644f44a.js"><link rel="prefetch" href="/assets/js/51.bed7cf61.js"><link rel="prefetch" href="/assets/js/510.b660946f.js"><link rel="prefetch" href="/assets/js/511.cb186c8f.js"><link rel="prefetch" href="/assets/js/512.d176170f.js"><link rel="prefetch" href="/assets/js/513.18a1afa5.js"><link rel="prefetch" href="/assets/js/514.4a33e931.js"><link rel="prefetch" href="/assets/js/515.8496bd4d.js"><link rel="prefetch" href="/assets/js/516.529b9476.js"><link rel="prefetch" href="/assets/js/517.289738f1.js"><link rel="prefetch" href="/assets/js/518.eda39556.js"><link rel="prefetch" href="/assets/js/519.85b42de5.js"><link rel="prefetch" href="/assets/js/52.22a7da62.js"><link rel="prefetch" href="/assets/js/520.8b531775.js"><link rel="prefetch" href="/assets/js/521.8e4b66fb.js"><link rel="prefetch" href="/assets/js/522.050bfc55.js"><link rel="prefetch" href="/assets/js/523.e973f1e0.js"><link rel="prefetch" href="/assets/js/524.fd160738.js"><link rel="prefetch" href="/assets/js/525.5a4bc307.js"><link rel="prefetch" href="/assets/js/526.361bc4fa.js"><link rel="prefetch" href="/assets/js/527.06ee9d73.js"><link rel="prefetch" href="/assets/js/528.22d9b118.js"><link rel="prefetch" href="/assets/js/529.b7592c16.js"><link rel="prefetch" href="/assets/js/53.767f3bbf.js"><link rel="prefetch" href="/assets/js/530.ad96d564.js"><link rel="prefetch" href="/assets/js/531.61905243.js"><link rel="prefetch" href="/assets/js/532.11aebf61.js"><link rel="prefetch" href="/assets/js/533.0cddb226.js"><link rel="prefetch" href="/assets/js/534.d1c9cc36.js"><link rel="prefetch" href="/assets/js/54.d1f9eec8.js"><link rel="prefetch" href="/assets/js/55.aa90e812.js"><link rel="prefetch" href="/assets/js/56.4d7f81f2.js"><link rel="prefetch" href="/assets/js/57.3c540e8d.js"><link rel="prefetch" href="/assets/js/58.fc1c23f2.js"><link rel="prefetch" href="/assets/js/59.c7a7a4c0.js"><link rel="prefetch" href="/assets/js/6.056886f1.js"><link rel="prefetch" href="/assets/js/60.40e590ae.js"><link rel="prefetch" href="/assets/js/61.e400b323.js"><link rel="prefetch" href="/assets/js/62.bbb30ef1.js"><link rel="prefetch" href="/assets/js/63.c31cd2bb.js"><link rel="prefetch" href="/assets/js/64.62321188.js"><link rel="prefetch" href="/assets/js/65.44adc2b3.js"><link rel="prefetch" href="/assets/js/66.b5740d4f.js"><link rel="prefetch" href="/assets/js/67.76c3624c.js"><link rel="prefetch" href="/assets/js/68.6dd982d4.js"><link rel="prefetch" href="/assets/js/69.eb1390ec.js"><link rel="prefetch" href="/assets/js/7.a96cecf9.js"><link rel="prefetch" href="/assets/js/70.8c882bdd.js"><link rel="prefetch" href="/assets/js/71.001c968a.js"><link rel="prefetch" href="/assets/js/72.ae4ca075.js"><link rel="prefetch" href="/assets/js/73.d6ae6a27.js"><link rel="prefetch" href="/assets/js/74.fba60e37.js"><link rel="prefetch" href="/assets/js/75.0b1bf7bf.js"><link rel="prefetch" href="/assets/js/76.ee19d90e.js"><link rel="prefetch" href="/assets/js/77.fe8ac5f0.js"><link rel="prefetch" href="/assets/js/78.ae09432e.js"><link rel="prefetch" href="/assets/js/79.c1b10e2d.js"><link rel="prefetch" href="/assets/js/80.ae809538.js"><link rel="prefetch" href="/assets/js/81.ca11b4c7.js"><link rel="prefetch" href="/assets/js/82.308b98e4.js"><link rel="prefetch" href="/assets/js/83.355d9dde.js"><link rel="prefetch" href="/assets/js/84.a2b61d48.js"><link rel="prefetch" href="/assets/js/85.cf992fda.js"><link rel="prefetch" href="/assets/js/86.f547c7ea.js"><link rel="prefetch" href="/assets/js/87.2b02746a.js"><link rel="prefetch" href="/assets/js/88.e939aad5.js"><link rel="prefetch" href="/assets/js/89.b000c42a.js"><link rel="prefetch" href="/assets/js/90.dadef001.js"><link rel="prefetch" href="/assets/js/91.c0fe2e32.js"><link rel="prefetch" href="/assets/js/92.e09650b9.js"><link rel="prefetch" href="/assets/js/93.427c7280.js"><link rel="prefetch" href="/assets/js/94.90fc5f58.js"><link rel="prefetch" href="/assets/js/95.13d42b3a.js"><link rel="prefetch" href="/assets/js/96.d0a2ea8c.js"><link rel="prefetch" href="/assets/js/97.cc9a0eda.js"><link rel="prefetch" href="/assets/js/98.3c9f1b7e.js"><link rel="prefetch" href="/assets/js/99.0420dcbc.js"><link rel="prefetch" href="/assets/js/vendors~docsearch.33b2b47d.js">
    <link rel="stylesheet" href="/assets/css/0.styles.40c15e22.css">
  </head>
  <body>
    <div id="app" data-server-rendered="true"><div class="theme-container"><header class="navbar"><div class="sidebar-button"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" role="img" viewBox="0 0 448 512" class="icon"><path fill="currentColor" d="M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z"></path></svg></div> <a href="/" class="home-link router-link-active"><img src="/logo.png" alt="鱼皮的编程宝典" class="logo"> <span class="site-name can-hide">鱼皮的编程宝典</span></a> <div class="links"><div class="search-box"><input aria-label="Search" autocomplete="off" spellcheck="false" value=""> <!----></div> <nav class="nav-links can-hide"><div class="nav-item"><a href="/学习路线/" class="nav-link">
  学习路线
</a></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="自学之路" class="dropdown-title"><span class="title">自学之路</span> <span class="arrow down"></span></button> <button type="button" aria-label="自学之路" class="mobile-dropdown-title"><span class="title">自学之路</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/自学之路/#大学经历" class="nav-link">
  大学经历
</a></li><li class="dropdown-item"><!----> <a href="/自学之路/#求职经历" class="nav-link">
  求职经历
</a></li><li class="dropdown-item"><!----> <a href="/自学之路/#职场工作" class="nav-link">
  职场工作
</a></li><li class="dropdown-item"><!----> <a href="/自学之路/#创作经历" class="nav-link">
  创作经历
</a></li><li class="dropdown-item"><!----> <a href="/自学之路/#创业经历" class="nav-link">
  创业经历
</a></li><li class="dropdown-item"><!----> <a href="/自学之路/#生活日常" class="nav-link">
  生活日常
</a></li></ul></div></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="编程分享" class="dropdown-title"><span class="title">编程分享</span> <span class="arrow down"></span></button> <button type="button" aria-label="编程分享" class="mobile-dropdown-title"><span class="title">编程分享</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/编程分享/#入门必看-学习路线" class="nav-link">
  入门必看-学习路线
</a></li><li class="dropdown-item"><!----> <a href="/编程分享/#学习指南" class="nav-link">
  学习指南
</a></li><li class="dropdown-item"><!----> <a href="/编程分享/#开发经验" class="nav-link">
  开发经验
</a></li><li class="dropdown-item"><!----> <a href="/编程分享/#求职经验" class="nav-link">
  求职经验
</a></li><li class="dropdown-item"><!----> <a href="/编程分享/#职场经验" class="nav-link">
  职场经验
</a></li><li class="dropdown-item"><!----> <a href="/编程分享/#技术分享" class="nav-link">
  技术分享
</a></li><li class="dropdown-item"><!----> <a href="/编程分享/#实战教程" class="nav-link">
  实战教程
</a></li><li class="dropdown-item"><!----> <a href="/编程分享/#其他" class="nav-link">
  其他
</a></li><li class="dropdown-item"><!----> <a href="/编程分享/#📚-项目教程" class="nav-link">
  项目教程
</a></li><li class="dropdown-item"><!----> <a href="/编程分享/#个人作品" class="nav-link">
  个人作品
</a></li><li class="dropdown-item"><!----> <a href="/编程分享/#🎁-编程资源" class="nav-link">
  编程资源
</a></li><li class="dropdown-item"><!----> <a href="/编程分享/#🌐-科技科普" class="nav-link">
  科技科普
</a></li></ul></div></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="项目实战" class="dropdown-title"><span class="title">项目实战</span> <span class="arrow down"></span></button> <button type="button" aria-label="项目实战" class="mobile-dropdown-title"><span class="title">项目实战</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/项目实战/OJ 判题系统.html" class="nav-link">
  OJ 判题系统
</a></li><li class="dropdown-item"><!----> <a href="/项目实战/智能 BI 平台.html" class="nav-link">
  智能 BI 平台
</a></li><li class="dropdown-item"><!----> <a href="/项目实战/聚合搜索平台.html" class="nav-link">
  聚合搜索平台
</a></li><li class="dropdown-item"><!----> <a href="/项目实战/API 开放平台.html" class="nav-link">
  API 开放平台
</a></li><li class="dropdown-item"><!----> <a href="/项目实战/伙伴匹配系统.html" class="nav-link">
  伙伴匹配系统
</a></li><li class="dropdown-item"><!----> <a href="/项目实战/用户中心项目.html" class="nav-link">
  用户中心项目
</a></li><li class="dropdown-item"><!----> <a href="/项目实战/Java 后端万用项目模板.html" class="nav-link">
  Java 后端万用项目模板
</a></li></ul></div></div><div class="nav-item"><a href="/知识碎片/" class="nav-link">
  知识碎片
</a></div><div class="nav-item"><a href="/编程导航/" class="nav-link">
  🔥 编程导航
</a></div><div class="nav-item"><a href="/产品服务/" class="nav-link">
  产品服务
</a></div><div class="nav-item"><a href="/作者/" class="nav-link">
  作者
</a></div> <a href="https://github.com/liyupi/codefather" target="_blank" rel="noopener noreferrer" class="repo-link">
    GitHub
    <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></nav></div></header> <div class="sidebar-mask"></div> <aside class="sidebar"><nav class="nav-links"><div class="nav-item"><a href="/学习路线/" class="nav-link">
  学习路线
</a></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="自学之路" class="dropdown-title"><span class="title">自学之路</span> <span class="arrow down"></span></button> <button type="button" aria-label="自学之路" class="mobile-dropdown-title"><span class="title">自学之路</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/自学之路/#大学经历" class="nav-link">
  大学经历
</a></li><li class="dropdown-item"><!----> <a href="/自学之路/#求职经历" class="nav-link">
  求职经历
</a></li><li class="dropdown-item"><!----> <a href="/自学之路/#职场工作" class="nav-link">
  职场工作
</a></li><li class="dropdown-item"><!----> <a href="/自学之路/#创作经历" class="nav-link">
  创作经历
</a></li><li class="dropdown-item"><!----> <a href="/自学之路/#创业经历" class="nav-link">
  创业经历
</a></li><li class="dropdown-item"><!----> <a href="/自学之路/#生活日常" class="nav-link">
  生活日常
</a></li></ul></div></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="编程分享" class="dropdown-title"><span class="title">编程分享</span> <span class="arrow down"></span></button> <button type="button" aria-label="编程分享" class="mobile-dropdown-title"><span class="title">编程分享</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/编程分享/#入门必看-学习路线" class="nav-link">
  入门必看-学习路线
</a></li><li class="dropdown-item"><!----> <a href="/编程分享/#学习指南" class="nav-link">
  学习指南
</a></li><li class="dropdown-item"><!----> <a href="/编程分享/#开发经验" class="nav-link">
  开发经验
</a></li><li class="dropdown-item"><!----> <a href="/编程分享/#求职经验" class="nav-link">
  求职经验
</a></li><li class="dropdown-item"><!----> <a href="/编程分享/#职场经验" class="nav-link">
  职场经验
</a></li><li class="dropdown-item"><!----> <a href="/编程分享/#技术分享" class="nav-link">
  技术分享
</a></li><li class="dropdown-item"><!----> <a href="/编程分享/#实战教程" class="nav-link">
  实战教程
</a></li><li class="dropdown-item"><!----> <a href="/编程分享/#其他" class="nav-link">
  其他
</a></li><li class="dropdown-item"><!----> <a href="/编程分享/#📚-项目教程" class="nav-link">
  项目教程
</a></li><li class="dropdown-item"><!----> <a href="/编程分享/#个人作品" class="nav-link">
  个人作品
</a></li><li class="dropdown-item"><!----> <a href="/编程分享/#🎁-编程资源" class="nav-link">
  编程资源
</a></li><li class="dropdown-item"><!----> <a href="/编程分享/#🌐-科技科普" class="nav-link">
  科技科普
</a></li></ul></div></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="项目实战" class="dropdown-title"><span class="title">项目实战</span> <span class="arrow down"></span></button> <button type="button" aria-label="项目实战" class="mobile-dropdown-title"><span class="title">项目实战</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/项目实战/OJ 判题系统.html" class="nav-link">
  OJ 判题系统
</a></li><li class="dropdown-item"><!----> <a href="/项目实战/智能 BI 平台.html" class="nav-link">
  智能 BI 平台
</a></li><li class="dropdown-item"><!----> <a href="/项目实战/聚合搜索平台.html" class="nav-link">
  聚合搜索平台
</a></li><li class="dropdown-item"><!----> <a href="/项目实战/API 开放平台.html" class="nav-link">
  API 开放平台
</a></li><li class="dropdown-item"><!----> <a href="/项目实战/伙伴匹配系统.html" class="nav-link">
  伙伴匹配系统
</a></li><li class="dropdown-item"><!----> <a href="/项目实战/用户中心项目.html" class="nav-link">
  用户中心项目
</a></li><li class="dropdown-item"><!----> <a href="/项目实战/Java 后端万用项目模板.html" class="nav-link">
  Java 后端万用项目模板
</a></li></ul></div></div><div class="nav-item"><a href="/知识碎片/" class="nav-link">
  知识碎片
</a></div><div class="nav-item"><a href="/编程导航/" class="nav-link">
  🔥 编程导航
</a></div><div class="nav-item"><a href="/产品服务/" class="nav-link">
  产品服务
</a></div><div class="nav-item"><a href="/作者/" class="nav-link">
  作者
</a></div> <a href="https://github.com/liyupi/codefather" target="_blank" rel="noopener noreferrer" class="repo-link">
    GitHub
    <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></nav>  <ul class="sidebar-links"><li><a href="/%E7%BC%96%E7%A8%8B%E5%88%86%E4%BA%AB/" class="sidebar-link">入门必看 - 学习路线</a></li><li><section class="sidebar-group collapsable depth-0"><p class="sidebar-heading"><span>入门必看-学习路线</span> <span class="arrow right"></span></p> <!----></section></li><li><section class="sidebar-group collapsable depth-0"><p class="sidebar-heading"><span>学习指南</span> <span class="arrow right"></span></p> <!----></section></li><li><section class="sidebar-group collapsable depth-0"><p class="sidebar-heading"><span>开发经验</span> <span class="arrow right"></span></p> <!----></section></li><li><section class="sidebar-group collapsable depth-0"><p class="sidebar-heading"><span>求职经验</span> <span class="arrow right"></span></p> <!----></section></li><li><section class="sidebar-group collapsable depth-0"><p class="sidebar-heading"><span>职场经验</span> <span class="arrow right"></span></p> <!----></section></li><li><section class="sidebar-group collapsable depth-0"><p class="sidebar-heading"><span>技术分享</span> <span class="arrow right"></span></p> <!----></section></li><li><section class="sidebar-group collapsable depth-0"><p class="sidebar-heading"><span>实战教程</span> <span class="arrow right"></span></p> <!----></section></li><li><section class="sidebar-group collapsable depth-0"><p class="sidebar-heading"><span>项目教程</span> <span class="arrow right"></span></p> <!----></section></li><li><section class="sidebar-group collapsable depth-0"><p class="sidebar-heading"><span>编程资源</span> <span class="arrow right"></span></p> <!----></section></li><li><section class="sidebar-group collapsable depth-0"><p class="sidebar-heading"><span>科技科普</span> <span class="arrow right"></span></p> <!----></section></li><li><section class="sidebar-group collapsable depth-0"><p class="sidebar-heading"><span>其他</span> <span class="arrow right"></span></p> <!----></section></li></ul> </aside> <main class="page"> <div class="content"><div style="width:100%"><div class="theme-default-content custom-content content__default"><h1 id="揭秘软件开发的达摩克利斯之剑"><a href="#揭秘软件开发的达摩克利斯之剑" class="header-anchor">#</a> 揭秘软件开发的达摩克利斯之剑</h1> <blockquote><p>本文作者：<a href="https://yuyuanweb.feishu.cn/wiki/Abldw5WkjidySxkKxU2cQdAtnah" target="_blank" rel="noopener noreferrer">程序员鱼皮<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></p> <p>本站地址：<a href="https://codefather.cn" target="_blank" rel="noopener noreferrer">https://codefather.cn<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></p></blockquote> <p>为什么你的程序总是出现 bug？</p> <p>凭什么让改 bug 占据了你大部分的时间？</p> <p>看完本文，保证你能设计出更稳定的程序，摆脱 bug 的缠绕，做项目更安心！</p> <p><img alt="" data-src="https://pic.yupi.icu/5563/202311071427667.jpeg" loading="lazy" class="lazy"></p> <hr> <p>记得我在学校的时候，做的那些项目，不是为了应付课程作业，就是为了参加比赛时展示用，因此对项目的质量要求非常低。</p> <p>到底有多低呢？</p> <p>大部分的项目，只要基本的功能可以使用，就算完成了，完全不考虑任何的异常情况。甚至只要能成功运行一次，让我截几张图放到 PPT 或者实验报告里，足够向老师交差或者应付比赛答辩就行。</p> <p>那项目出现 bug 怎么办呢？</p> <ul><li>如果测试的时候发现有些功能不可用，那很简单，不管他，直接 PS 一张正常运行的图就行。</li> <li>如果比赛的时候发现有些功能不可用，那也很简单，把锅甩给 “现场网络不好” 就行。</li></ul> <p><img alt="" data-src="https://pic.yupi.icu/5563/202311071427737.jpeg" loading="lazy" class="lazy"></p> <p>但是，这些 “小技巧” 在企业中是行不通的，企业级项目必须为企业带来实际的价值，容不得半点马虎和欺骗。</p> <p>我第一次进入企业实习时，还保留着自己在学校开发项目的狼性 🐺，只要能够完成基本功能就行，保证以最快的速度完成开发。</p> <p>有一天，当我洋洋得意准备早点下班时，测试同学走过来跟我说。</p> <p>“喂，你的程序有 bug，这里用户下单怎么金额是负的？”</p> <p><img alt="" data-src="https://pic.yupi.icu/5563/202311071427416.png" loading="lazy" class="lazy">写个 bug</p> <p>对于我一个初入职场的小白，这是人生中第一次有人说我的代码有 bug，我有问题，我不对劲。</p> <p>当时，我脑海的第一个念头竟然是怎么把这个 bug 糊弄过去，而不是怎么去更正！看来我已经养成了非常不好的习惯。</p> <p>那之后几天，我又连续收到了测试提出的多个 bug，然后将他们一个个改正。如果将这样一个漏洞百出的程序发布上线，带来的损失是不可估量的，现在想想仍心有余悸。</p> <p><img alt="" data-src="https://pic.yupi.icu/5563/202311071427438.png" loading="lazy" class="lazy">二八原则 开发 1 天，改 bug 4 天</p> <p>这件事之后，我意识到，在企业中开发项目，不能只追求开发时的效率，还要注重项目的稳定性，否则带来的额外返工时间远比开发时节省的时间要长，而且会影响同事对你的看法。如果将开发时产生的 bug 遗留到线上，后果更是不堪设想！</p> <p>后来，在字节跳动和腾讯这两家大公司工作后，我进一步认识到了项目稳定性有多重要，并且积累了更多只有在大公司才能学到的提升项目稳定性的经验。</p> <p>我总结了 <strong>10 个</strong>开发中通常不会考虑到的风险点，以及 <strong>16 个</strong>减少风险、提升项目稳定性的方法，分享给大家~</p> <p><img alt="" data-src="https://pic.yupi.icu/5563/202311071427352.jpeg" loading="lazy" class="lazy"></p> <p>在分享这些之前，先讲个故事。</p> <h4 id="达摩克利斯之剑"><a href="#达摩克利斯之剑" class="header-anchor">#</a> 达摩克利斯之剑</h4> <p>古希腊传说中，达摩克利斯是公元前 4 世纪意大利叙拉古的僭主（古希腊统治者独有的称号）狄奥尼修斯二世的朝臣，他非常喜欢奉承狄奥尼修斯。</p> <p>他奉承道：“作为一个拥有权力和威信的伟人，狄奥尼修斯实在很幸运。”</p> <p>于是狄奥尼修斯提议与他交换一天的身份，那他就可以尝试到首领的命运。</p> <p>在晚上举行的宴会里，达摩克利斯非常享受成为国王的感觉。当晚餐快结束的时候，他抬头才注意到王位上方仅用一根马鬃悬挂着的利剑。他立即失去了对美食和美女的兴趣，并请求僭主放过他，他再也不想得到这样的幸运。</p> <p><img alt="" data-src="https://pic.yupi.icu/5563/202311071427317.png" loading="lazy" class="lazy"></p> <blockquote><p>达摩克利斯之剑</p></blockquote> <p>这个故事告诉我们什么呢？</p> <ol><li>在和平安宁之后，时刻存在着危险与不安。</li> <li>当一个人获取多少荣誉和地位，他都要付出同样多的代价。</li> <li>地位越高，看似越安全，实则越危险。</li> <li>居安思危，对随时可能带来的严重后果，要做到谨慎。</li></ol> <p>那么这和软件开发又有什么关系呢？下面就让我来揭秘软件开发中的达摩克利斯之剑。</p> <h4 id="危机四伏"><a href="#危机四伏" class="header-anchor">#</a> 危机四伏</h4> <p>“在和平安宁之后，时刻存在着危险与不安。”</p> <p><img alt="" data-src="https://pic.yupi.icu/5563/202311071427065.jpeg" loading="lazy" class="lazy"></p> <p>软件开发正是如此，表面上机器是 “死” 的，只会按照人输入的指令或编好的程序来执行，一成不变，听话的很。好像我们写好代码扔到机器上后，就可以高枕无忧。</p> <p>但真的是这样么？<strong>我们真的可以信任机器和程序么？</strong></p> <p>其实，在程序世界中危机四伏，人为因素、环境因素等可能都会对我们的程序产生影响。因此，我们必须时刻坚守软件开发的<strong>不信任原则</strong>，保持 <code>overly pessimistic</code>（过于悲观），把和程序有关的一切请求、服务、接口、返回值、机器、框架、中间件等等都当做不可信的，步步为营、处处设防。</p> <p><img alt="" data-src="https://pic.yupi.icu/5563/202311071427070.png" loading="lazy" class="lazy"></p> <blockquote><p>程序世界里的不信任原则</p></blockquote> <p>那为什么写个代码要这么小心翼翼，什么都不信任呢？</p> <h4 id="大项目的苦衷"><a href="#大项目的苦衷" class="header-anchor">#</a> 大项目的苦衷</h4> <p>“当一个人获取多少荣誉和地位，他都要付出同样多的代价。”</p> <p>软件开发中，项目价值越大，需要承受的压力也越大，来听听大项目的苦衷吧。</p> <p><img alt="" data-src="https://pic.yupi.icu/5563/202311071427357.jpeg" loading="lazy" class="lazy"></p> <p>我是一个身价过亿的大项目，每天服务着上千万的用户，帮助他们获得知识与快乐。</p> <p>我的小伙伴们只看到我身上的光环和荣耀，但是他们看不到我背负的压力和风险，今天终于有机会和大家倾诉我的苦衷了。</p> <p>记得很多年前，我还是个孩子，只有几个小主人开发我，那段时间，我成长的很快。虽然只有几十个人使用我，但我感到非常轻松和快乐，偶尔偷会儿懒，也不会被人发现。</p> <p>后来，我的功能越来越多，越来越强大。每天有数之不尽的新面孔来和我打招呼，并享受我提供的服务。渐渐地，更多开发者在我身上留下了印记，我感觉自己正在变得复杂，也开始感受到了压力。我再也找不到机会偷懒，因为我一旦休息，就会让我的主人们损失一比不小的财富。</p> <p>如今，我已经是一个成熟的大项目了，每天有上千万的用户依赖我，我终于拥有了更大的价值，却也增加了很多烦恼，感受到了更大的危险。</p> <p><img alt="" data-src="https://pic.yupi.icu/5563/202311071427661.jpeg" loading="lazy" class="lazy"></p> <p>首先，同时服务千万用户，每秒钟都可能会有几十万、甚至几百万个请求需要我来处理，因此我必须每时每刻无休止地高负载工作，且不说休息，哪怕稍微慢了一点，就会遭到用户的投诉，主人们也会因此受到批评。</p> <p>我的运行，必须依靠很多兄弟们的支撑，因此我必须和兄弟们好好相处，哪怕一个兄弟倒了，我都会受到影响。</p> <p>在我强大的实力背后，有一颗非常脆弱的心。经历了那么多次的强化和改造，我的功能逐渐变多的同时，也因此被植入了各种框架和插件，体积像滚雪球一般越来越大，不知道什么时候就会爆炸。以至于主人们每次改动我时都要万分谨慎，我的成长也变得十分缓慢。</p> <p><img alt="" data-src="https://pic.yupi.icu/5563/202311071427120.jpeg" loading="lazy" class="lazy"></p> <blockquote><p>复杂</p></blockquote> <p>然而最让我感到恐惧的，是那些坏家伙们！</p> <p>他们和正常的用户不同，有的不断制造请求，试图将我击垮。有的绕到我的背后，试图直接控制我。有的对我虎视眈眈，监视并记录我的一举一动。还有的尝试各种非法操作，想从我身上牟取暴利。</p> <p>作为一个大项目真是太累了，我不知道我还能坚持多久。</p> <h4 id="真的可信么"><a href="#真的可信么" class="header-anchor">#</a> 真的可信么？</h4> <p>“地位越高，看似越安全，实则越危险。”</p> <p>如今是一个软件开源和共享的时代，我们在开发项目时，或多或少会使用到网上现有的资源，比如依赖包、工具、组件、框架、接口、现成的云服务等等，这些资源能够大大提升我们的开发效率。</p> <p>就拿云服务来说，几乎已经成了我们开发必备的资源，以前我们想要做一个网站，可能需要自己买一台物理服务器，然后连通网络，再把项目部署上去。而如今，直接登录大公司的云官网（像腾讯云、阿里云），然后租一台云服务器就行了，非常省事。</p> <p><img alt="" data-src="https://pic.yupi.icu/5563/202311071427907.png" loading="lazy" class="lazy"></p> <blockquote><p>云服务</p></blockquote> <p>再说说现在主流的开发框架，以前做一个简单的网站界面可能只会使用 <code>HTML</code>、<code>CSS</code>、<code>JavaScript</code> 这三种最基础的技术，而如今，网站的样式和交互越来越复杂，我们不得不使用一些知名的框架来提升开发效率，比如 <code>Vue</code> 和 <code>React</code>。</p> <p>听起来好像没有任何问题，你也根本不会去怀疑什么，因为<strong>我们天生带着对大公司，或者说是对名气的信任</strong>。</p> <p>但是，你知道么，当你决定使用其他人的资源时，你就已经把项目系统的部分掌控权、甚至可能是半条命，都交出去了。</p> <p>那么不妨思考一下，你使用的这些资源，真的可信么？</p> <p><strong>下面 10 个问题，可能改变你对开发的认知。</strong></p> <p><img alt="" data-src="https://pic.yupi.icu/5563/202311071427033.jpeg" loading="lazy" class="lazy"></p> <h4 id="_1-开发工具可信么"><a href="#_1-开发工具可信么" class="header-anchor">#</a> 1. 开发工具可信么？</h4> <p>我们通常是在大而全的开发工具中编写代码，比如 <code>JetBrains IDEA</code> 或者 <code>Vscode</code>。很多刚开始写代码的同学、甚至是一些经验丰富的老手，都对开发工具保持绝对的信任。</p> <p>比如你在键盘上敲击 <code>a</code>，那编辑器界面上显示的一定是 <code>a</code>。</p> <p>但是，由于内存不足等种种原因，<strong>开发工具其实也会抽风</strong>。</p> <p>比如你想要调用某个函数，通常敲击函数名前几个字母后，开发工具就会自动给你提示完整的函数名，但如果开发工具没有给你提示，你首先怀疑的是这个函数不存在，而不是编辑器没有按预期给出提示。遇到这种情况，可以稍等编辑器一下，或者进一步确认函数是否真的不存在，而不是立刻创建一个新的函数。</p> <p>又或是项目无法运行，怎么排查都觉得没问题，这时不妨重新启动下开发工具，或者清理一下缓存，说不定项目就能正常运行了！</p> <p>还有很多非常有意思的情况，比如编辑器一片大红，各种提示错误，但是项目依然能成功运行。</p> <p><img alt="" data-src="https://pic.yupi.icu/5563/202311071427622.jpeg" loading="lazy" class="lazy"></p> <blockquote><p>为什么不能运行？为什么能运行？</p></blockquote> <p>因此，不要绝对相信开发工具。</p> <h4 id="_2-开源项目可信么"><a href="#_2-开源项目可信么" class="header-anchor">#</a> 2. 开源项目可信么？</h4> <p>现在是一个软件开源的时代，在 <code>GitHub</code> 等开源项目平台上能够找到大量优秀的开源项目，好的开源项目甚至可以得到 10 万多个关注，那这些知名的开源项目可信么？</p> <p>不完全可信！从每个开源项目的 Issues 就能看出这点，而且通常越大的项目，被发现的问题越多，比如 <code>Vue</code> 项目，累积提出并关闭了 8000 多个问题。</p> <p><img alt="" data-src="https://pic.yupi.icu/5563/202311071427478.png" loading="lazy" class="lazy"></p> <blockquote><p>Vue 项目的问题</p></blockquote> <p>我记得自己有一次使用知名的开源服务器 <code>Tomcat</code>，就遇到了 bug，每次接受到特定的请求都会报错。刚开始我根本没有怀疑是 <code>Tomcat</code> 的问题，而是绞尽脑汁地想自己的代码哪里写错了。后来经过反复的排查和搜索，终于确认了就是 <code>Tomcat</code> 本身的 bug！</p> <p>虽然开源项目并不完全可信，但是相对于私有项目而言，所有对项目感兴趣的同学可以共同发现项目中的问题，并加以解决，在一定程度上还是能够提高项目的可靠性的。</p> <h4 id="_3-依赖库可信么"><a href="#_3-依赖库可信么" class="header-anchor">#</a> 3. 依赖库可信么？</h4> <p>我们在开发项目时，通常会用到大量的依赖库。直接在官方依赖源（比如 <code>Maven</code> 和 <code>npm</code>）搜索依赖库，然后使用包管理器，用一行命令或者编写配置文件就能够让其自动安装依赖，非常方便。</p> <p><strong>但是，这些发布到官方源的依赖库，就可信么？</strong></p> <p>且不说基本每个开发者都有机会发布依赖库到官方，就算是互联网大公司的依赖库，也未必可信。</p> <p>给我印象最深刻的就是阿里巴巴的 <code>JSON</code> 序列化类库 <code>fastjson</code>，几乎无人不知、无人不晓，因为其极快的解析速度广受好评。但是，这个库被多次曝光存在高危漏洞，可以让攻击者远程执行命令！一般的开发者根本不会发现这点，从而给项目带来了极大的危害。</p> <p><img alt="" data-src="https://pic.yupi.icu/5563/202311071427912.jpeg" loading="lazy" class="lazy"></p> <p>因此，在选用依赖库的时候，要做好充分的调研，尽量确认依赖库的安全，并且保证不要和已有的依赖冲突。</p> <h4 id="_4-编程语言可信么"><a href="#_4-编程语言可信么" class="header-anchor">#</a> 4. 编程语言可信么？</h4> <p><code>Java</code> 是一种强类型语言，具有健壮性。这句话我相信所有学过 <code>Java</code> 的同学都再熟悉不过了。但是，强类型编程语言就一定可信么？</p> <p>这里可能有同学就要表示怀疑了，如果我们一直使用的最基础最底层的编程语言都存在 bug，那我们怎么去相信建立在这些编程语言上的框架呢？</p> <p>然而真相是，所有的编程语言都有 bug！而且基本每次编程语言发布新版本时都会对一些历史 bug 进行修正。就 <code>Java</code> 而言，甚至还有一个专门记录 bug 的数据库！</p> <p><img alt="" data-src="https://pic.yupi.icu/5563/202311071427116.png" loading="lazy" class="lazy">Java Bug 数据库</p> <p>但是，对于大多数开发者来说，我相信即使在程序中偶然触发了编程语言本身的 bug，也没有足够的自信去质疑，而是直接修改代码来绕过。</p> <p>确实，质疑编程语言需要一定的基础和知识储备，但是一旦发现了程序中莫名其妙的问题，建议大家不要直接忽略，可以花一些时间去探索研究，说不定你就成功地发现了一个重大的 bug，也能够加深对这门编程语言的理解。</p> <h4 id="_5-服务器可信么"><a href="#_5-服务器可信么" class="header-anchor">#</a> 5. 服务器可信么？</h4> <p>服务器是项目赖以生存的宿主，服务器的性能和稳定性将直接影响到项目进程。</p> <p>无论是个人开发者还是企业，通常都会直接租用大公司提供的云服务器来部署项目，省去了自己搭建和维护的麻烦。</p> <p>但是大公司的云服务器就可信么？</p> <p>不完全可信！即使现在的云服务器提供商都承诺自己的服务 SLA（服务级别协议）可以达到 5 个 9（99.999% 一年约宕机 5 分钟），甚至 6 个 9（99.9999% 一年约宕机 30 秒），但是仍然存在一定的风险。</p> <p>有一个非常有名的案例，在 2013 年，中国最大的社交通讯软件出现大规模的故障，多达几亿用户受到影响。原因竟然是，市政道路建设的一个不注意，把网络光缆挖断了，就导致该软件所在服务器的无法访问。</p> <p>除了可用性的不可信之外，可能还有一些安全隐私方面的问题。当然云服务商通常是不会获取用户的数据的，但也没有办法绝对相信他们。毕竟数据的隐私对企业至关重要，这也是为什么大的公司都会搭建属于自己的服务器机房和网络。</p> <p><img alt="" data-src="https://pic.yupi.icu/5563/202311071427745.jpeg" loading="lazy" class="lazy"></p> <blockquote><p>机房</p></blockquote> <h4 id="_6-数据库可信么"><a href="#_6-数据库可信么" class="header-anchor">#</a> 6. 数据库可信么？</h4> <p>企业中的大多数业务数据都是存放在数据库中的，通过项目后端程序来操作和查询数据库中的数据。</p> <p>和服务器一样，我们可以使用软件自己搭建数据库，比如 <code>MySQL</code>，也可以直接租用大公司的云数据库，那么数据库可信么？</p> <p>其实在企业后端项目中，数据库通常是性能瓶颈，相对比较脆弱，当访问并发量大一点时，数据库的查询性能就会下降，严重时可能整个宕机！即使是大公司提供的云数据库服务，遇到慢查询（需要较长时间的查询）时，可能也无从应对。</p> <p>数据库中的数据其实也未必可信，有时管理员的一个误操作，不小心删除数据或添加了一条错误数据，可能就会影响用户，造成损失。更有甚者，竟然删库跑路，不讲码德！</p> <p><img alt="" data-src="https://pic.yupi.icu/5563/202311071427079.jpeg" loading="lazy" class="lazy"></p> <blockquote><p>删库跑路</p></blockquote> <p>因此，不要过于信任数据库，应当使用缓存之类的技术帮助数据库分担压力，并定期备份。否则一旦数据库宕机或数据丢失，带来的损失是不可估量的！</p> <h4 id="_7-缓存服务可信么"><a href="#_7-缓存服务可信么" class="header-anchor">#</a> 7. 缓存服务可信么？</h4> <p>缓存是开发高性能程序必备的技术，通过将数据库等查询较慢的数据存放在内存中，直接从内存中读取数据，以提升查询性能。有了缓存之后，项目不仅能够支持更多人同时查询数据，还能够保护数据库。</p> <p>目前比较主流的缓存技术有 <code>Redis</code>、<code>Memcached</code> 等，可以自己在服务器搭建，也可以直接租用大公司提供的云缓存服务。</p> <p><img alt="" data-src="https://pic.yupi.icu/5563/202311071427090.png" loading="lazy" class="lazy">存储键值对的缓存</p> <p>那么缓存服务是否可信呢？</p> <p>项目的并发量不是特别大的话，一般的缓存技术就足以支持了，但是如果项目的量级很大，可能缓存也无法承受住压力，严重时就会宕机。而一旦缓存挂掉，大量的查询命令会直接请求数据库，于是数据库也会在瞬间挂掉，严重时还会导致整个项目瘫痪！</p> <p>因此，在使用缓存时，需要对并发量进行评估，通过搭建集群和数据同步保证高可用性。此外，还要预防<strong>缓存雪崩、缓存穿透、缓存击穿</strong>等问题，简单解释一下。</p> <p>缓存雪崩：指大量缓存在同一时间过期，请求都访问不到缓存，全部打到数据库上，导致数据库挂掉。</p> <p>缓存穿透：持续访问缓存中不存在的 key 导致请求直接打到数据库上，导致数据库挂掉。</p> <p>缓存击穿：一个被大量请求高频访问的热点 key 突然过期，导致请求瞬间全部打到数据库上，导致数据库挂掉。</p> <p>如果不预防这三个问题，即使是租用大公司的缓存服务，也一样吹弹可破。</p> <p><img alt="" data-src="https://pic.yupi.icu/5563/202311071427966.jpeg" loading="lazy" class="lazy"></p> <blockquote><p>雪崩</p></blockquote> <h4 id="_8-对象存储可信么"><a href="#_8-对象存储可信么" class="header-anchor">#</a> 8. 对象存储可信么？</h4> <p>项目中，经常会有用户上传图片或文件的功能，这类数据通常较大，用数据库存储不太方便。虽然我们可以将文件直接存到服务器上，但更好的做法是使用专门的对象存储服务。</p> <p>可以简单地把对象存储当做一个大的文件夹，我们可以通过它直接上传和下载文件。大的云服务商也都提供了专业的对象存储服务，而无需自己搭建，那么对象存储可信么？</p> <p>一般情况下，上传到对象存储的文件是不会缺失或丢失的，而且还可以将已上传的数据进行跨园区同步，起到备份的作用。</p> <p><img alt="" data-src="https://pic.yupi.icu/5563/202311071427277.png" loading="lazy" class="lazy"></p> <blockquote><p>跨园区同步</p></blockquote> <p>但是，记得有一次，上传到对象存储上的文件和源文件竟然不一致，大小足足少了 1M。起初我以为是文件上传到对象存储时，会自动被压缩，但是将对象存储中的文件下载到本地后，发现的确和源文件不一致！虽然出现这种情况的概率极其小，但从那一刻起，我再也不相信对象存储了。</p> <p>再用自己的真实经历来聊聊对象存储的跨园区同步。因为个人负责的业务比较重要，万一单个机房整体挂掉，可能分分钟是几十万元的损失！因此我为对象存储配置了自动跨园区同步，将文件先上传至广州机房，然后数据会自动同步到上海机房，且运维同学承诺自动同步的延迟不超过 15 分钟。</p> <p>我相信大部分开发者配置数据同步后也就不管了，相信它一定会自动同步的。结果后面我编写程序去做同步监控、对比数据时，发现经常出现数据未同步的情况，比例高达 10%！</p> <p><img alt="" data-src="https://pic.yupi.icu/5563/202311071427134.jpeg" loading="lazy" class="lazy"></p> <p>因此，不能完全相信对象存储，虽然大部分情况下大公司的对象存储服务很可靠，但不能确保万无一失。尤其是同步备份的场景下，是否真的同步成功了，又有多少同学关心过呢？不妨写个程序去验证和保障。</p> <h4 id="_9-api-接口可信么"><a href="#_9-api-接口可信么" class="header-anchor">#</a> 9. API 接口可信么？</h4> <p>在开发中，我们经常会调用其他系统提供的 API 接口来轻松实现某种功能。比如查询某地的天气，可以直接调用其他人提供的天气查询接口，而无需自己编写。我们也可以提供 API 接口给其他人使用，尤其是在微服务架构中，各服务之间都是以接口调用的形式实现交互协作的。</p> <p>几乎所有的 API 接口提供者都会说自己的接口有多安全、请放心使用，那么 API 接口真的可信么？</p> <p><strong>其实，API 接口是最不可信的资源！</strong></p> <p>首先，API 接口的提供方可以是任何开发者，很难通过他们的一面之词来确定接口的稳定性和安全性。</p> <p>即使这个接口性能很高、也很安全，但是你并不了解有多少人和你在同时使用这个接口，也许只有你，又也许是 100 万个其他的开发者呢？在这个竞争条件下，接口的 <code>qps</code>（query per second 每秒查询数）还能达到预期么？接口返回时长真的不会超时么？</p> <p>更有甚者，偷偷地把 API 接口改动了，却没有给调用者发送通知，这样接口的调用方全部都会调用失败，严重影响项目的运行！</p> <p>因此，我们在调用第三方 API 接口时，一定要慎重、慎重、再慎重！</p> <p>此外，如果我们是 API 接口的提供者，也要注意保护好自己的 API 接口，避免同时被太多的开发者调用，导致接口挂掉。</p> <p><img alt="" data-src="https://pic.yupi.icu/5563/202311071427579.jpeg" loading="lazy" class="lazy"></p> <blockquote><p>API 存在复杂的调用关系</p></blockquote> <h4 id="_10-serverless-可信么"><a href="#_10-serverless-可信么" class="header-anchor">#</a> 10. Serverless 可信么？</h4> <p>如果说服务器不可信，那我们干脆就不租服务器了，直接租用大公司提供的 Serverless 服务来作为项目的后台不就行了？</p> <p>Serverless 指无服务器架构，并不是真的不需要服务器，而是将项目接口的部署、运维等需要对服务器的操作交给服务商去做，让开发者无需关心服务器，专心写代码就好。</p> <p><img alt="" data-src="https://pic.yupi.icu/5563/202311071427021.png" loading="lazy" class="lazy"></p> <blockquote><p>docker 容器</p></blockquote> <p>听起来非常爽，那 Serverless可信么？</p> <p>使用 Serverless，虽然能够大大提升开发和运维效率，但是其相对服务器等资源而言，更不可信！</p> <p>首先，Serverless 本身就是部署在服务器上的，难免会受到服务器的影响。</p> <p>其次，Serverless 服务不会长期保持应用的状态，而是随着请求的到来而启动，存在冷启动时期，虽然也有很多相关的优化和解决方案，但仍无法精确地保证接口的性能，尤其是在高并发场景下，性能往往达不到预期。</p> <p>最重要的是，当你选择使用 Serverless 服务时，你就和某云服务提供商绑定了，后续想要迁移是非常困难的！试想一下，你项目的所有功能都交给别人来维护，真的是好事么？一旦云服务提供商改造了架构或接口，你的代码也要随之改动，而这种改动却不是由自己控制的！</p> <p>当然，Serverless 具有非常多的优点，也是云计算技术发展的必然趋势，只是希望大家在使用前，考虑到那些可能的风险，并做好应对措施。</p> <p><img alt="" data-src="https://pic.yupi.icu/5563/202311071427621.jpeg" loading="lazy" class="lazy"></p> <blockquote><p>云计算时代</p></blockquote> <p>总结：正是因为我们太过信任那些名气大、看似安全的资源，所以其背后的危险才更难以被察觉，带来的后果往往也更致命！</p> <h4 id="防御性编程"><a href="#防御性编程" class="header-anchor">#</a> 防御性编程</h4> <p>“居安思危，对随时可能带来的严重后果，要做到谨慎。”</p> <p>在软件开发中，虽然项目表面上能够正常运行，但风险无处不在，因此我们要学习防御性编程思想。把自己当成一个杠精，不要相信任何人，尽力去发现程序中的风险，积极防御。</p> <p>下面给大家分享 16 个防御性编程的方法，学习之后，能够大大减少程序中的风险。</p> <p><img alt="" data-src="https://pic.yupi.icu/5563/202311071427929.jpeg" loading="lazy" class="lazy"></p> <blockquote><p>祈祷性编程</p></blockquote> <h4 id="_1-编程习惯"><a href="#_1-编程习惯" class="header-anchor">#</a> 1. 编程习惯</h4> <p>要减少程序中的风险，首先要养成良好的编程习惯。</p> <p>首先，在写代码时，一定要保持良好的心态，不要仓促或者以完成任务的心态去写代码。如果仅仅是为了完成需求，那么很有可能不会注意到代码中的风险，甚至是发现了风险也懒得去修补，这样确实能够节约开发的时间，但是后面出现问题后，你还是要花费更多的时间去排查、沟通和修复 bug。拔苗助长，适得其反。</p> <p>在写代码时，如果在一个地方多次使用相同且复杂的变量名或字符串，建议不要手动去敲，而是用大家最喜欢的 “复制粘贴”，防止因为手误而导致的 bug。</p> <p><img alt="" data-src="https://pic.yupi.icu/5563/202311071427529.jpeg" loading="lazy" class="lazy"></p> <blockquote><p>复制粘贴一把梭</p></blockquote> <p>此外，我们在代码中应该加强对返回值的检查，并且选择安全的语法和数据结构，避免使用被废弃的语法。不同的编程语言也有不同的最佳编程习惯，比如在 <code>Java</code> 语言中，应该对所有可能为 <code>NULL</code> 的变量进行检查，防止 <code>NPE</code>（NULL Pointer Error 空指针异常），在开发多线程程序时，选用线程安全的 <code>ConcurrentHashMap</code> 而不是 <code>HashMap</code> 等等。还可以利用 <code>Assert</code>（断言）来保证程序运行中的变量值符合预期。</p> <p>推荐使用一个自带检查功能的编辑器来书写代码，在我们编写代码时会自动检查出错误，还能给出好的编码风格的建议，能够大大减少开发时的风险。此外，在代码提交前，一定要多次检查代码，尤其是那些复制粘贴过来的文件，经常会出现遗漏的修改。提交代码后，也可以找有经验的同事帮忙阅读和检查下代码（代码审查），进一步保证没有语法和逻辑错误。</p> <p><img alt="" data-src="https://pic.yupi.icu/5563/202311071427372.png" loading="lazy" class="lazy"></p> <blockquote><p>编辑器语法检查和提示</p></blockquote> <h4 id="_2-异常处理"><a href="#_2-异常处理" class="header-anchor">#</a> 2. 异常处理</h4> <p>程序的运行风云变幻，同一段代码在不同情况下也可能会产生不同的结果，甚至是异常。因此很多主流的编程语言中都有异常处理机制，比如在 <code>Java</code> 中，先用 <code>try</code> 捕获异常、再用 <code>catch</code> 处理异常、最后用 <code>finally</code> 释放资源和善后。</p> <p>在编程时，要合理利用异常处理机制，来防御代码中可能出现的种种问题。通常在异常处理中，我们会记录错误日志、执行错误上报和告警、重试等。</p> <p>比如不信任数据库，那就在查询和操作数据时添加异常处理，一旦数据库抽风导致操作失败，就在日志中记录失败信息，并通过邮件、短信等告警方式通知到开发者，就能第一时间发现问题并排查。必要时还可以实现自动重试，省去一部分人工操作。</p> <p><img alt="" data-src="https://pic.yupi.icu/5563/202311071427581.png" loading="lazy" class="lazy"></p> <blockquote><p>异常啦</p></blockquote> <h4 id="_3-请求校验"><a href="#_3-请求校验" class="header-anchor">#</a> 3. 请求校验</h4> <p>所有的请求都是不可信的，哪怕是在公司内网，也有可能因为一些失误，导致发出了错误的请求。</p> <p>因此我们编写的每个接口，在实现具体的业务逻辑前，一定要先对请求参数加上校验，下面列举几种常见的校验方式：</p> <ol><li>参数类型校验：比如请求参数应该是 <code>Integer</code> 整型而不是 <code>Long</code> 长整数类型。</li> <li>值合法性校验：比如整数的范围大于等于 0、字符串长度大于 5，或者满足某种特定格式，比如手机号、身份证等。</li> <li>用户权限校验：很多接口需要登录用户或者管理员才能调用，因此必须通过请求参数（请求头）来判断当前用户的身份，被一个普通用户下载了 VIP 付费电影肯定是不合理的！</li></ol> <p><img alt="" data-src="https://pic.yupi.icu/5563/202311071427595.jpeg" loading="lazy" class="lazy"></p> <h4 id="_4-流量控制"><a href="#_4-流量控制" class="header-anchor">#</a> 4. 流量控制</h4> <p>上面提到，所有的请求都是不可信的，不仅仅是请求的值，还有请求的量和频率。对于所有接口，都要限制它的调用频率，防止接口被大量瞬时的请求刷爆。对于付费接口，还要防止用户对接口的请求数超过原购买数。</p> <p>此外，还有一种容易被忽视的情况，假如你的接口 A 中又调用了其他人的接口 B，也许你的接口 A 自身的逻辑能够承受每秒 1000 个请求，但是你确定接口 B 可以承受么？</p> <p>因此，需要进行流量控制，不仅仅是预防接口被刷爆，还可以保护内部的服务和调用。</p> <p>什么，你说你的接口很牛逼，每秒能抗 100 万个请求，也没有调用其他的服务，那我就找 100 万 + 1 个人同时请求你的接口，看你怕不怕！</p> <p><img alt="" data-src="https://pic.yupi.icu/5563/202311071427471.jpeg" loading="lazy" class="lazy"></p> <blockquote><p>DDOS 分布式拒绝服务攻击</p></blockquote> <p><strong>常用的流量控制按照不同的粒度可分为：</strong></p> <ol><li>用户流控：限制每个用户在一定时间内对某个接口的调用数。</li> <li>接口流控：限制一定时间内某个接口的总调用数。</li> <li>单机流控：限制一定时间内单台服务器上的项目所有接口的总调用数。</li> <li>分布式流控：限制一定时间内项目所有服务器的总请求数。</li></ol> <p>当然，除了上面提到的几种方式外，流控可以非常灵活，也有很多优秀的限流工具。比如 <code>Java</code> 语言 <code>Guava</code> 库的 <code>RateLimiter</code> 令牌桶单机限流、阿里的 <code>Sentinel</code> 分布式限流框架等。</p> <p><img alt="" data-src="https://pic.yupi.icu/5563/202311071427678.png" loading="lazy" class="lazy"></p> <blockquote><p>Sentinel 流控面板</p></blockquote> <h4 id="_5-回滚"><a href="#_5-回滚" class="header-anchor">#</a> 5. 回滚</h4> <p>有时，我们对项目的操作可能是错误的，可能是人工操作，也可能是机器操作，从而导致了一些线上故障。这时，可以选择回滚。</p> <p>回滚是指撤销某个操作，将项目还原到之前的状态，这里介绍几种常见的回滚操作。</p> <p><strong>数据回滚</strong></p> <p>有时，我们想要批量插入数据，但是数据插入到一半时，程序突然出现异常，这个时候我们就需要把之前插入成功的数据进行回滚，就好像什么都没发生过一样。否则可能存在数据不一致的风险。</p> <p>最常见的方式就是使用事务来处理数据库的批量操作，当出现异常时，执行数据库客户端的回滚方法即可。</p> <p><strong>配置回滚</strong></p> <p>如果将项目的配置信息，比如数据库链接地址，写死到代码中，一旦配置错了或者地址发生变更，就要重新修改代码，非常麻烦。</p> <p>比较好的方式是将配置发布到配置中心进行管理，让项目去动态读取配置中心的配置。如果不小心发布了错误的配置，可以直接在配置中心进行回滚，将配置还原。</p> <p><strong>发布回滚</strong></p> <p>没有人能保证自己的代码正确无误，很多时候，项目在测试环境验证时没有发现任何问题，但是一上线，就漏洞百出。这就说明我们最新发布的代码是存在问题的。</p> <p>这时，最简单的做法就是进行版本回滚，将之前能够正常运行的代码重新打包发布。大公司一般都有自己的项目发布平台，能够使用界面一键回滚，自动发布以前版本的项目包。</p> <p><img alt="" data-src="https://pic.yupi.icu/5563/202311071427691.png" loading="lazy" class="lazy"></p> <h4 id="_6-多级缓存"><a href="#_6-多级缓存" class="header-anchor">#</a> 6. 多级缓存</h4> <p>上面提到，缓存对项目是非常重要的，不仅是提升性能的利器，也是数据库的保护伞。</p> <p>但如果缓存挂掉怎么办呢？</p> <p>有两种方案，第一种是为缓存搭建集群，从而保证缓存的高可用。</p> <p><img alt="" data-src="https://pic.yupi.icu/5563/202311071427820.png" loading="lazy" class="lazy"></p> <blockquote><p>Redis 集群</p></blockquote> <p>但是一切都不可信，集群也有可能挂掉！</p> <p>那么可以用第二种方案，一级缓存挂掉，我们就再搞一个二级缓存顶上！</p> <p>通常，在高并发项目中，我们会设计多级缓存，即分布式缓存 + 本地缓存。当请求需要获取数据时，先从分布式缓存（比如 <code>Redis</code>） 中查询，如果分布式缓存集体宕机，那就从本地缓存中获取数据。这样，即使缓存挂掉，也能够帮助系统支撑一段时间。</p> <p>这里可能和一些多级缓存的设计不同，有时，我们会把本地缓存作为一级缓存，缓存一些热点数据，本地缓存找不到值时，才去访问分布式缓存。这种设计主要解决的问题是，减少对分布式缓存的请求量，并进一步提升性能，和上面的设计目的不同。</p> <p><img alt="" data-src="https://pic.yupi.icu/5563/202311071427261.png" loading="lazy" class="lazy"></p> <blockquote><p>多级缓存设计</p></blockquote> <h4 id="_7-服务熔断和降级"><a href="#_7-服务熔断和降级" class="header-anchor">#</a> 7. 服务熔断和降级</h4> <p>每年的双十一，我们会准时守着屏幕上的抢购页面，只为等待那一个 “请稍后再试！”</p> <p><img alt="" data-src="https://pic.yupi.icu/5563/202311071427194.jpeg" loading="lazy" class="lazy"></p> <p>我们的项目其实远比想象的要脆弱，很多服务经常因为各种原因出现问题。比如搞活动时，大量用户同时访问会导致对项目服务的请求增多，如果项目顶不住压力，就会挂掉。</p> <p>为了防止这种风险，我们可以采用<strong>服务降级</strong>策略，如果系统实在无法为所有用户提供服务，那就退而求其次，给用户直接返回一个 “友好的” 提示或界面，而不是强行让项目顶着压力过劳死。</p> <p>配合<strong>服务熔断</strong>技术，可以根据系统的负载等指标来动态开启或关闭降级。比如机器的 CPU 被占用爆满时，就开启降级，直接返回错误；当机器 CPU 恢复正常时，再正常返回数据、执行操作。</p> <p><code>Hystrix</code> 就是比较有名的微服务熔断降级框架。</p> <p><img alt="" data-src="https://pic.yupi.icu/5563/202311071427732.png" loading="lazy" class="lazy"></p> <blockquote><p>Hystrix</p></blockquote> <h4 id="_8-主动检测"><a href="#_8-主动检测" class="header-anchor">#</a> 8. 主动检测</h4> <p>上面提到，即使是大公司的同步服务，也可能会出现同步不及时甚至是数据丢失的情况。因此，为了进一步保证同步成功、数据的准确，我们可以<strong>主动检测</strong>。</p> <p>比如编写一个定时脚本或者任务，每隔一段时间去检查原地址和目标地址的数据是否一致，或者通过一些逻辑来检查数据是否正确。当然也可以在每次数据同步结束后都立即去检测，更加保险。</p> <p><img alt="" data-src="https://pic.yupi.icu/5563/202311071427775.png" loading="lazy" class="lazy"></p> <blockquote><p>主动检测</p></blockquote> <h4 id="_9-数据补偿"><a href="#_9-数据补偿" class="header-anchor">#</a> 9. 数据补偿</h4> <p>当检测出数据不一致后，我们就要进行数据补偿，比如将没有同步的数据再次进行同步、将不一致的数据进行更新等。</p> <p>除了用来解决主动检测出的数据不一致，数据补偿也被广泛用于业务设计和架构设计中。</p> <p>比如调用某个接口查询数据失败后，停顿一段时间，然后自动重试，或者从其他地方获取数据。又如消息队列的生产者发送消息失败时，应该自动进行补发和记录，而不是直接把这条消息作废。</p> <p>数据补偿的思想本质上是保证数据的最终一致性，数据出错不可怕，知错能改就是好孩子。这种思想也被广泛应用于分布式事务等场景中。</p> <p><img alt="" data-src="https://pic.yupi.icu/5563/202311071427216.png" loading="lazy" class="lazy"></p> <h4 id="_10-数据备份"><a href="#_10-数据备份" class="header-anchor">#</a> 10. 数据备份</h4> <p>数据是企业的生命，因此我们必须尽可能地保证数据的安全和完整。</p> <p>很多同学会把自己重要的文件存放在多个地方，比如自己的电脑、网盘上等等。同样，在软件开发中，我们也应该把重要的数据复制多份，作为副本存放在不同的地方。这样，即使一台服务器挂了，也可以从其他的服务器上获取到数据，减少了风险。</p> <p><img alt="" data-src="https://pic.yupi.icu/5563/202311071427373.jpeg" loading="lazy" class="lazy"></p> <blockquote><p>数据备份</p></blockquote> <h4 id="_11-心跳机制"><a href="#_11-心跳机制" class="header-anchor">#</a> 11. 心跳机制</h4> <p>接口可是个复杂多变的家伙，如果我们的项目依赖其他的接口来完成功能，那么最好保证该接口一直活着，否则可能会影响项目的运行。</p> <p>举个例子，我们在使用银行卡支付时，肯定需要调用银行提供的接口来获取银行卡的余额信息，如果这个接口挂了，获取不到余额，用户也就无法支付，也就损失了一笔收入！</p> <p>因此，我们需要时刻和重要的接口保持联系，防止他们不小心死了。可以采用心跳机制，定时调用该接口或者发送一个心跳包，来判断该接口是否仍然存活。一旦调用超时或者失败，可以立刻进行排查和处理，从而大大减少了事故的影响时长。</p> <p><img alt="" data-src="https://pic.yupi.icu/5563/202311071427124.png" loading="lazy" class="lazy"></p> <blockquote><p>心跳检测</p></blockquote> <h4 id="_12-冗余设计"><a href="#_12-冗余设计" class="header-anchor">#</a> 12. 冗余设计</h4> <p>在系统资源和容量评估时，我们要做一些冗余设计，比如数据库目前的总数据量有 1G，那么如果要将数据库的数据同步到其他存储（比如 <code>Elasticsearch</code>）时，至少要多预留一倍的存储空间，即 2G，来应对后面可能的数据增长。业务的发展潜力越大，冗余的倍数也可以越多，但也要注意不要过分冗余，毕竟资源也是很贵的啊！</p> <p>其实，冗余设计是一种重要的设计思想。当我们设计业务或者系统架构时，不能只局限于当前的条件，而是要考虑到以后的发展，选择一种相对便于扩展的模式。否则之后项目越做越大，每一次对项目的改动都步履维艰。</p> <p><img alt="" data-src="https://pic.yupi.icu/5563/202311071427984.png" loading="lazy" class="lazy"></p> <h4 id="_13-弹性扩缩容"><a href="#_13-弹性扩缩容" class="header-anchor">#</a> 13. 弹性扩缩容</h4> <p>梦想还是要有的，说不定突然，我们原先只有 100 人使用的小项目突然就火了，有几十万新用户要来使用。</p> <p>但是，由于我们的项目只部署在一台服务器上，根本无法支撑那么多人，直接挂掉，导致这些用户非常扫兴，再也不想用我们的项目了。</p> <p><img alt="" data-src="https://pic.yupi.icu/5563/202311071427608.jpeg" loading="lazy" class="lazy"></p> <blockquote><p>梦想破碎了</p></blockquote> <p>这也是常见的风险，我们可以使用弹性扩缩容技术，系统会根据当前项目的使用和资源占用情况自动扩充或缩减资源。</p> <p>比如当系统压力较大时，多分配几台机器（容器），当系统压力较小时，减少几台机器。这样不仅能够有效应对突发的流量增长，还能够在平时节约成本，并省去了人工分配调整机器的麻烦。</p> <h4 id="_14-异地多活"><a href="#_14-异地多活" class="header-anchor">#</a> 14. 异地多活</h4> <p>前面提到，服务器是不可信的，别说一个服务器挂掉，由于一些天灾人祸，整个机房都有可能集体挂掉！</p> <p>和备份不同，异地多活是指在不同城市建立独立的数据中心，正常情况下，用户无论访问哪一个地点的业务系统，都能够得到正确的服务，即同时有多个 “活” 的服务。</p> <p>而某个地方业务异常的时候，用户能够访问其他地方正常的业务系统，从而获得正确的服务。</p> <p>如此一来，即使广州的机房跨了，咱还有上海的，上海的跨了，咱还有北京的。</p> <p>同时活着的服务越多，系统就越可靠，但同时成本也越高、越复杂，因此几乎都是大公司才做异地多活。千万不要让正常情况下的投入大于故障发生的损失！</p> <p><img alt="" data-src="https://pic.yupi.icu/5563/202311071427021.jpeg" loading="lazy" class="lazy"></p> <blockquote><p>饿了么异地多活技术实现（一)总体介绍</p></blockquote> <h4 id="_15-监控告警"><a href="#_15-监控告警" class="header-anchor">#</a> 15. 监控告警</h4> <p>项目的运行不可能一直正常，但是我们不可能 24 小时盯着电脑屏幕来监视项目的运行情况吧？又不能完全不管项目，出了 bug 等着用户来投诉。</p> <p>因此，最好的方式是给业务添加监控告警，当程序出现异常时，信息会上报到监控平台，并第一时间给开发者发送通知。还可以通过监控平台实时查看项目的运行情况，出了问题也能更快地定位。</p> <p><img alt="" data-src="https://mmbiz.qpic.cn/mmbiz_jpg/mngWTkJEOYIzRQbbyYDNts7MywRcySISLx6ibPhKtDCDtP4Ou4u75QDNhUAcnsdWwuCvBdE2P4mKU8m1c8oK2zw/640?wx_fmt=jpeg&amp;wxfrom=5&amp;wx_lazy=1&amp;wx_co=1" loading="lazy" class="lazy"></p> <blockquote><p>Grafana 监控平台</p></blockquote> <h4 id="_16-线上诊断和热修复"><a href="#_16-线上诊断和热修复" class="header-anchor">#</a> 16. 线上诊断和热修复</h4> <p>既然程序世界一切都不可信，危险无处不在，那么干脆就做最坏的打算，假设线上程序一定会出 bug。</p> <p>既然防不胜防，那就严阵以待，在 bug 出现时用最快的速度修复它，来减少影响。</p> <p>通常，我们要改 bug，也需要经历改动代码、提交代码、合并代码、打包构建、发布上线等一系列流程。等流程走完了，可能系统都透心凉了。</p> <p>为提高效率，我们可以使用线上诊断和热修复技术。在出现 bug 时，先用线上诊断工具轻松获取项目的各运行状态和代码执行信息，提升排查效率。发现问题后，使用热修复技术直接修改运行时的代码，无需重新构建和重启项目！</p> <p>在 <code>Java</code> 中，我们可以使用阿里开源的诊断工具 <code>Arthas</code>，同时支持线上热修复功能。也可以自己编写脚本来实现，但是相对复杂一些。</p> <p><img alt="" data-src="https://mmbiz.qpic.cn/mmbiz_png/mngWTkJEOYIzRQbbyYDNts7MywRcySISZJ4eSEbyrUKrFO5XlCbnzVuEIwDIHicsOzrBnxjt96ZGtic67ZKQwicicQ/640?wx_fmt=png&amp;wxfrom=5&amp;wx_lazy=1&amp;wx_co=1" loading="lazy" class="lazy"></p> <blockquote><p>Arthas Logo</p></blockquote> <hr> <p>看到这里，肯定有同学会吐槽，怎么写个程序要考虑那么多和功能无关的问题。本来五分钟就能写完的代码，现在可能一个小时都写不完！</p> <p><img alt="" data-src="https://pic.yupi.icu/5563/202311071427468.png" loading="lazy" class="lazy"></p> <blockquote><p>超凶</p></blockquote> <p>其实，并不是所有的项目都要做到绝对的安全（当然我们也做不到），而是我们应该时刻保持居安思危的思想，把防御性编程当做自己的习惯。</p> <p><strong>实际情况下，要根据项目的量级、受众、架构、紧急程度等因素来综合评估将项目做到何种程度的安全，而不是过度设计、杞人忧天。</strong></p> <p>让我们把时间慢下来，在开发前先冷静思考，预见并规避风险，不要让达摩克利斯之剑落下。</p></div> <footer class="page-edit" style="margin:0;"><div class="edit-link"><a href="https://github.com/liyupi/codefather/edit/master/编程分享/技术分享/技术实践/揭秘软件开发的达摩克利斯之剑.md" target="_blank" rel="noopener noreferrer">完善页面</a> <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></div> <div class="last-updated"><span class="prefix">最近更新:</span> <span class="time">11/11/2023, 8:17:47 PM</span></div></footer> <!----></div> <div class="toc-container-sidebar"><div class="pos-box"><div class="icon-arrow"></div> <div class="scroll-box" style="max-height:86vh"><div style="font-weight:bold;">揭秘软件开发的达摩克利斯之剑</div> <hr> <div class="toc-box"><!----></div></div></div></div></div>  <main class="footer"> <div class="copy-right"><span class="name"> 编程导航   |     </span> <a target="_blank" rel="noreferrer">
          
        </a></div></main></main> <aside class="page-sidebar"> <div class="page-side-toolbar"></div>  </aside></div><div class="global-ui"><!----></div></div>
    <script src="/assets/js/app.12ab4756.js" defer></script><script src="/assets/js/2.fd06b53e.js" defer></script><script src="/assets/js/268.e18ed861.js" defer></script>
  </body>
</html>
